﻿%define pg.RootNamespace ParserGenerator
%define pg.SyntacticAnalysisNamespace ParserGenerator.Languages.JSON
%define pg.SourceContextNamespace ParserGenerator.Input
%define pg.OutputNamespace ParserGenerator.Output

%define pg.UsePythonLikeIndentation False
%define pg.indented False

%define pg.parser.classname JSONParser
%define pg.parser.namespace ParserGenerator.Languages.JSON

%define pg.parser.usings (
    ParserGenerator.Collections,
)

%token IntegerLiteral ("integer literal") : System.Numerics.BigInteger
%token FloatLiteral ("float literal") : float64
%token StringLiteral ("string literal") : string
%xstart STRING_LITERAL

JsonValue:
    Null
    Boolean (value : bool)
    Integer (value : System.Numerics.BigInteger)
    Float (value : float64)
    String (value : string)
    Array (value : [JsonValue])
    Object (value : [(string, JsonValue)])

%%

JsonValue as JsonValue:
    'null' => JsonValue.NewNull(@@)
    'true' => JsonValue.NewBoolean(True, @@)
    'false' => JsonValue.NewBoolean(False, @@)
    IntegerLiteral => JsonValue.NewInteger($1, @@)
    FloatLiteral => JsonValue.NewFloat($1, @@)
    StringLiteral => JsonValue.NewString($1, @@)
    '[' JsonValue % ',' ']' => JsonValue.NewArray($2, @@)
    '{' KeyValuePair % ',' '}' => JsonValue.NewObject($2, @@)

KeyValuePair as (string, JsonValue):
    StringLiteral ':' JsonValue => ($1, $3)

%%

nl = [ '\n' '\v' '\f' '\r' '\x85' '\u2028' '\u2029' ]
ws = [ '\t' ' ' '\xa0' '\u2000'-'\u200a' '\u202f' '\u205f' '\u3000' ]+
nnl = [^ '\n' '\v' '\f' '\r' '\x85' '\u2028' '\u2029' ]

<*>:
    nl =>: pass
    ws =>: pass

decimal_constant = ['+' '-']? ['1'-'9'] ['0'-'9']*

<*>:
    decimal_constant =>: ScanIntegerLiteral(); yield IntegerLiteral

decimal_floating_constant = ['+' '-']? ( fractional_constant exponent_part?
                            | ['0'-'9']+ exponent_part )
fractional_constant = ['0'-'9']* '.' ['0'-'9']+ | ['0'-'9']+ '.'
exponent_part = ['e' 'E'] ['+' '-']? ['0'-'9']+

<*>:
    'nan' =>: TokenValue = double.NaN; yield FloatLiteral
    'inf' =>: TokenValue = double.PositiveInfinity; yield FloatLiteral
    '+inf' =>: TokenValue = double.PositiveInfinity; yield FloatLiteral
    '-inf' =>: TokenValue = double.NegativeInfinity; yield FloatLiteral
    decimal_floating_constant =>: ScanFloatLiteral(); yield FloatLiteral

hex = ['0'-'9' 'A'-'F' 'a'-'f']
hex_quad = hex hex hex hex

<*>:
    '"' =>: BeginString(STRING_LITERAL)

<STRING_LITERAL>:
    '"' =>: EndString(); yield StringLiteral
    "\\'" =>: AppendChar('\'')
    "\\\"" =>: AppendChar('"')
    "\\?" =>: AppendChar('?')
    "\\\\" =>: AppendChar('\\')
    "\\a" =>: AppendChar('\a')
    "\\b" =>: AppendChar('\b')
    "\\t" =>: AppendChar('\t')
    "\\n" =>: AppendChar('\n')
    "\\v" =>: AppendChar('\v')
    "\\f" =>: AppendChar('\f')
    "\\r" =>: AppendChar('\r')
    "\\" ['0'-'7']{1,3} =>: AppendEscape(-1, True)
    "\\x" hex+ =>: AppendEscape(-1, False)
    "\\u" hex_quad =>: AppendEscape(4, False)
    "\\U" hex_quad hex_quad =>: AppendEscape(8, False)
    nnl =>: AppendTokenString()
    default =>: raise UnterminatedStringLiteral()
